
#include "config/execution_manifest.h"
#include "config/core_affinity.h"
#include "common/exception.h"
#include "logger.h"

#include "nosal/log/logger.h"
#include "nosal/log/logging.h"

//#include "apd/manifestreader/manifest_reader.h"
//#include "apd/crc/crc.h"

#include <fstream>

namespace netaos {
namespace nosal {
namespace exec {
namespace internal {
namespace config {

namespace {

netaos::nosal::log::Logger& GetLogger()
{
    static netaos::nosal::log::Logger& logger
        = netaos::nosal::log::CreateLogger("EXMN", "Execution Manifest", netaos::nosal::exec::internal::kLogLevel);
    return logger;
}

}  // namespace

ExecutionManifest::ExecutionManifest(std::string manifest_path, const EnterExitTimeout& default_timeout)
    : reporting_behavior_(ExecutionStateReportingBehavior::kReports)  // default value, see TPS_MANI_01279
{
    constexpr auto kPlatformManifest = "executable.platform_application";
    constexpr auto kTimerResolution = "executable.timer_resolution_ns";
    constexpr auto kReportingBehavior = "executable.execution_state_reporting_behavior";
    constexpr auto kExecutableVersion = "executable.version";
    constexpr auto kStartupConfigs = "startup_configs";
    constexpr auto kProcessName = "process_name";
    constexpr auto kFunctionClusterAffiliation = "function_cluster_affiliation";
    constexpr auto kNumberOfRestartAttempts = "number_of_restart_attempts";
    constexpr auto kShallRunOn = "shall_run_on";
    constexpr auto kShallNotRunOn = "shall_not_run_on";
#if 0
    auto manifestRes = apd::manifestreader::OpenManifest(manifest_path);
    if (!manifestRes) {
        GetLogger().LogError() << "ExecutionManifest: error opening Manifest at " << manifest_path;
        return;
    }
    auto manifest = std::move(manifestRes).Value();

    auto platformAppRes = manifest->GetValue<bool>(kPlatformManifest);
    is_platform_application_ = platformAppRes.ValueOr(false);

    auto processName = manifest->GetValue<std::string>(kProcessName);
    process_name_ = netaos::nosal::core::nullopt;
    if (processName.HasValue()) {
        process_name_ = processName.Value();
    }

    auto executableVersion = manifest->GetValue<std::string>(kExecutableVersion);
    executableVersion_ = netaos::nosal::core::nullopt;
    if (executableVersion.HasValue()) {
        executableVersion_ = executableVersion.Value();
    } else {
        GetLogger().LogWarn() << "No Executable Version defined in Execution Manifest at" << manifest_path;
    }

    auto functionClusterAffiliationRes = manifest->GetValue<std::string>(kFunctionClusterAffiliation);
    if (functionClusterAffiliationRes) {
        functionClusterAffiliation_ = functionClusterAffiliationRes.Value();
    }

    auto coreAffinityShallRunOnRes = manifest->GetArray<CoreAffinity>(kShallRunOn);
    if (coreAffinityShallRunOnRes) {
        for (auto& coreAffinityShallRunOn : coreAffinityShallRunOnRes.Value()) {
            shall_run_on_.push_back(coreAffinityShallRunOn.core_);
        }
    }

    auto coreAffinityShallNotRunOnRes = manifest->GetArray<CoreAffinity>(kShallNotRunOn);
    if (coreAffinityShallNotRunOnRes) {
        for (auto& coreAffinityShallNotRunOn : coreAffinityShallNotRunOnRes.Value()) {
            shall_not_run_on_.push_back(coreAffinityShallNotRunOn.core_);
        }
    }

    auto timerRes = manifest->GetValue<uint64_t>(kTimerResolution);
    timer_resolution_ = std::chrono::nanoseconds(timerRes.ValueOr(0));

    auto reportingRes = manifest->GetValue<std::string>(kReportingBehavior);
    if (reportingRes) {
        reportingBehavior_ = ParseReportingBehavior(reportingRes.Value());
    }

    auto restartRes = manifest->GetValue<std::uint32_t>(kNumberOfRestartAttempts);
    numberOfRestartAttempts_ = restartRes.ValueOr(0);  // if not configured, 0 shall be set; see TPS_Manifest

    auto startupConfigsRes = manifest->GetArray<StartupConfiguration>(kStartupConfigs);
    if (!startupConfigsRes) {
        GetLogger().LogError() << "ExecutionManifest: error reading StartupConfigs at" << manifest_path;
        return;
    }
    startup_configs_ = std::move(startupConfigsRes).Value();

    for (StartupConfiguration& config : startup_configs_) {
        if (!config.enter_exit_timeout_) {
            config.enter_exit_timeout_ = default_timeout;
        }
    }

    if (startup_configs_.empty()) {
        GetLogger().LogError() << "ExecutionManifest: empty list of StartupConfigs at" << manifest_path;
    }

    crc32Value_ = CalculateCrc32Value(manifest_path);
#endif
}

ExecutionManifest::ExecutionStateReportingBehavior ExecutionManifest::ParseReportingBehavior(
    const std::string& value) const
{
    constexpr auto kReports = "reports";
    constexpr auto kDoesNotReport = "does_not_report";

    if (kReports == value) {
        return ExecutionManifest::ExecutionStateReportingBehavior::kReports;
    } else if (kDoesNotReport == value) {
        return ExecutionManifest::ExecutionStateReportingBehavior::kDoesNotReport;
    } else {
        GetLogger().LogError() << "Setting default value for ReportingBehavior due to misconfigured value:" << value;
        return ExecutionManifest::ExecutionStateReportingBehavior::kReports;
    }
}

std::uint32_t ExecutionManifest::CalculateCrc32Value(std::string manifest_path)
{
	
    std::ifstream file_input_stream(manifest_path.data());
    if (!file_input_stream.is_open()) {
        GetLogger().LogError() << "Failed to open file at" << manifest_path << ", set default CRC value 0.";
        return 0;
    }

    std::string input_string((std::istreambuf_iterator<char>(file_input_stream)), std::istreambuf_iterator<char>());
    std::vector<std::uint8_t> buffer(input_string.begin(), input_string.end());

//    apd::crc::BufferView buffer_view(buffer);
  //  return apd::crc::CRC::CalculateCRC32(buffer_view);
    return 0;
}

}  // namespace config
}  // namespace internal
}  // namespace exec
}  // namespace nosal
}  // namespace netaos
